/* Declarations for custom, native types.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_CUSTOM__
#define __KP_CUSTOM__

#include "interp.hpp"

KP_DECLS_BEGIN

struct visitor
{
  virtual void operator() (interpreter *, object)
    {
    }
};

struct custom_base : public finobj
{
  static const int code = typecode::CUSTOM;
  object *type_ptr;

  object type () const
    {
      return (*this->type_ptr);
    }

  virtual void do_visit (interpreter *, visitor&)
    {
    }

  void visit (interpreter *interp, visitor& vs)
    {
      vs (interp, this->type ());
      this->do_visit (interp, vs);
    }

  virtual void fill_type (interpreter *, object&, object&)
    {
    }

  static custom_base* alloc_raw (size_t size);

  static custom_base* xalloc (interpreter *interp, object *tp,
                              const char *name, custom_base *bp);

  static custom_base* xalloc (interpreter *interp, object *tp,
                              const char *name, uint32_t len, custom_base *bp);

  static custom_base* xalloc (interpreter *interp, object *tp,
                              object name, custom_base *bp);

  virtual ~custom_base ()
    {
    }
};

inline custom_base*
as_custom (object obj)
{
  /* We need to jump through finobj because that's where the method 'as_obj'
   * is called from. And since custom types have a vtable, we need to preserve
   * the original chain of casts. */
  return ((custom_base *)(finobj *)unmask (obj));
}

template <class T>
struct custom : public custom_base
{
  static object type_desc;

  static T* alloc (interpreter *interp, const char *name)
    {
      return ((T *)custom_base::xalloc (interp, &custom<T>::type_desc, name,
                                        custom_base::alloc_raw (sizeof (T))));
    }

  static T* alloc (interpreter *interp, const char *name, size_t len)
    {
      return ((T *)custom_base::xalloc (interp, &custom<T>::type_desc,
                                        name, len,
                                        custom_base::alloc_raw (sizeof (T))));
    }

  static T* alloc (interpreter *interp, object name)
    {
      return ((T *)custom_base::xalloc (interp, &custom<T>::type_desc, name,
                                        custom_base::alloc_raw (sizeof (T))));
    }

  static bool type_p (object obj)
    {
      return (itype (obj) == typecode::CUSTOM &&
        as_custom(obj)->type_ptr == &custom<T>::type_desc);
    }
};

template <class T>
object custom<T>::type_desc = 0;

KP_DECLS_END

#endif
